use crate::{
    ast::Expression,
    error::{CompilerError, Result},
    llvm_backend::LLVMBackend,
};
use inkwell::{values::BasicValueEnum, AddressSpace, IntPredicate};

impl<'ctx> LLVMBackend<'ctx> {

    /// Generate string.split(separator) method call
    /// Returns a Vector<String> containing the substrings split by the separator
    ///
    /// Example: "a,b,c".split(",") returns Vec["a", "b", "c"]
    pub(in crate::llvm_backend) fn generate_string_split_method(
        &mut self,
        var_name: &str,
        arguments: &[Expression],
    ) -> Result<BasicValueEnum<'ctx>> {
        // Validate arguments
        if arguments.len() != 1 {
            return Err(CompilerError::internal(
                "split() requires exactly one argument (separator: String)",
            ));
        }

        // Get the string pointer to split
        let string_ptr = if let Some(var_value) = self.variable_map.get(var_name) {
            var_value.into_pointer_value()
        } else {
            return Err(CompilerError::internal(&format!(
                "Undefined variable: {}",
                var_name
            )));
        };

        // Load the string pointer value (i8*)
        let i8_ptr_type = self.context.i8_type().ptr_type(AddressSpace::default());
        let string_value = self
            .builder
            .build_load(i8_ptr_type, string_ptr, "string_val")?;
        let string_ptr_value = string_value.into_pointer_value();

        // Get separator from argument (must be string)
        let separator_arg = &arguments[0];
        let separator = self.generate_expression(separator_arg)?;
        let separator_ptr = separator.into_pointer_value();

        // Get C library functions we need
        let i8_type = self.context.i8_type();
        let i32_type = self.context.i32_type();
        let i64_type = self.context.i64_type();

        // strlen function
        let strlen_fn_type = i32_type.fn_type(&[i8_ptr_type.into()], false);
        let strlen_fn = if let Some(func) = self.module.get_function("strlen") {
            func
        } else {
            self.module.add_function("strlen", strlen_fn_type, None)
        };

        // strstr function - finds substring in string
        let strstr_fn_type = i8_ptr_type.fn_type(&[i8_ptr_type.into(), i8_ptr_type.into()], false);
        let strstr_fn = if let Some(func) = self.module.get_function("strstr") {
            func
        } else {
            self.module.add_function("strstr", strstr_fn_type, None)
        };

        // malloc function
        let malloc_fn_type = i8_ptr_type.fn_type(&[i64_type.into()], false);
        let malloc_fn = if let Some(func) = self.module.get_function("malloc") {
            func
        } else {
            self.module.add_function("malloc", malloc_fn_type, None)
        };

        // memcpy function - copy memory
        let memcpy_fn_type = i8_ptr_type.fn_type(
            &[i8_ptr_type.into(), i8_ptr_type.into(), i64_type.into()],
            false,
        );
        let memcpy_fn = if let Some(func) = self.module.get_function("memcpy") {
            func
        } else {
            self.module.add_function("memcpy", memcpy_fn_type, None)
        };

        // Calculate separator length
        let sep_len_i32 = self
            .builder
            .build_call(strlen_fn, &[separator_ptr.into()], "sep_len_i32")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_int_value();
        let sep_len = self
            .builder
            .build_int_z_extend(sep_len_i32, i64_type, "sep_len")?;

        // Calculate string length
        let str_len_i32 = self
            .builder
            .build_call(strlen_fn, &[string_ptr_value.into()], "str_len_i32")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_int_value();
        let str_len = self
            .builder
            .build_int_z_extend(str_len_i32, i64_type, "str_len")?;

        // Create basic blocks
        let current_func = self.current_function.unwrap();
        let empty_sep_check = self
            .context
            .append_basic_block(current_func, "split_empty_sep_check");
        let empty_str_check = self
            .context
            .append_basic_block(current_func, "split_empty_str_check");
        let count_loop_start = self
            .context
            .append_basic_block(current_func, "split_count_loop_start");
        let count_loop_body = self
            .context
            .append_basic_block(current_func, "split_count_loop_body");
        let count_loop_end = self
            .context
            .append_basic_block(current_func, "split_count_loop_end");
        let alloc_vector = self
            .context
            .append_basic_block(current_func, "split_alloc_vector");
        let split_loop_start = self
            .context
            .append_basic_block(current_func, "split_loop_start");
        let split_loop_body = self
            .context
            .append_basic_block(current_func, "split_loop_body");
        let split_loop_end = self
            .context
            .append_basic_block(current_func, "split_loop_end");
        let add_last_part = self
            .context
            .append_basic_block(current_func, "split_add_last_part");
        let return_block = self
            .context
            .append_basic_block(current_func, "split_return");

        // Jump to empty separator check
        self.builder.build_unconditional_branch(empty_sep_check)?;

        // Empty separator check: if separator is empty, return vector with original string
        self.builder.position_at_end(empty_sep_check);
        let sep_is_empty = self.builder.build_int_compare(
            IntPredicate::EQ,
            sep_len,
            i64_type.const_int(0, false),
            "sep_is_empty",
        )?;

        // For empty separator case: create array with original string NOW
        let ptr_size = i64_type.const_int(8, false);
        let empty_sep_array = self
            .builder
            .build_call(malloc_fn, &[ptr_size.into()], "empty_sep_array")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();
        let i8_ptr_ptr_type = i8_ptr_type.ptr_type(AddressSpace::default());
        let empty_sep_array_typed = self
            .builder
            .build_bitcast(empty_sep_array, i8_ptr_ptr_type, "empty_sep_array_typed")?
            .into_pointer_value();
        self.builder
            .build_store(empty_sep_array_typed, string_ptr_value)?;

        self.builder
            .build_conditional_branch(sep_is_empty, return_block, empty_str_check)?;

        // Empty string check: if string is empty, return vector with empty string
        self.builder.position_at_end(empty_str_check);
        let str_is_empty = self.builder.build_int_compare(
            IntPredicate::EQ,
            str_len,
            i64_type.const_int(0, false),
            "str_is_empty",
        )?;

        // For empty string case: create array with empty string NOW
        let empty_str_array = self
            .builder
            .build_call(malloc_fn, &[ptr_size.into()], "empty_str_array")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();
        let empty_str_array_typed = self
            .builder
            .build_bitcast(empty_str_array, i8_ptr_ptr_type, "empty_str_array_typed")?
            .into_pointer_value();
        let empty_str = self.builder.build_global_string_ptr("", "empty_str")?;
        self.builder
            .build_store(empty_str_array_typed, empty_str.as_pointer_value())?;

        self.builder
            .build_conditional_branch(str_is_empty, return_block, count_loop_start)?;

        // Count loop: count how many times separator appears
        self.builder.position_at_end(count_loop_start);
        let count_alloca = self.builder.build_alloca(i64_type, "count")?;
        let current_pos_alloca = self.builder.build_alloca(i8_ptr_type, "current_pos")?;

        self.builder
            .build_store(count_alloca, i64_type.const_int(0, false))?;
        self.builder
            .build_store(current_pos_alloca, string_ptr_value)?;

        self.builder.build_unconditional_branch(count_loop_body)?;

        // Count loop body
        self.builder.position_at_end(count_loop_body);
        let current_pos = self
            .builder
            .build_load(i8_ptr_type, current_pos_alloca, "current_pos")?
            .into_pointer_value();

        // Find next occurrence using strstr
        let found_ptr = self
            .builder
            .build_call(
                strstr_fn,
                &[current_pos.into(), separator_ptr.into()],
                "found_ptr",
            )?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();

        // Check if found (not NULL)
        let null_ptr = i8_ptr_type.const_null();
        let is_found = self.builder.build_int_compare(
            IntPredicate::NE,
            found_ptr,
            null_ptr,
            "is_found",
        )?;
        self.builder
            .build_conditional_branch(is_found, count_loop_end, alloc_vector)?;

        // Count loop end: increment count and move position
        self.builder.position_at_end(count_loop_end);
        let count = self
            .builder
            .build_load(i64_type, count_alloca, "count")?
            .into_int_value();
        let new_count =
            self.builder
                .build_int_add(count, i64_type.const_int(1, false), "new_count")?;
        self.builder.build_store(count_alloca, new_count)?;

        // Move position past the separator
        let new_pos = unsafe {
            self.builder
                .build_gep(i8_type, found_ptr, &[sep_len], "new_pos")?
        };
        self.builder.build_store(current_pos_alloca, new_pos)?;

        self.builder.build_unconditional_branch(count_loop_body)?;

        // Allocate vector: allocate space for (count + 1) strings
        self.builder.position_at_end(alloc_vector);
        let final_count = self
            .builder
            .build_load(i64_type, count_alloca, "final_count")?
            .into_int_value();
        let vec_size =
            self.builder
                .build_int_add(final_count, i64_type.const_int(1, false), "vec_size")?;

        // Allocate array of string pointers (i8**)
        let array_size = self
            .builder
            .build_int_mul(vec_size, ptr_size, "array_size")?;
        let strings_array = self
            .builder
            .build_call(malloc_fn, &[array_size.into()], "strings_array")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();

        // Cast to i8**
        let strings_array_typed = self
            .builder
            .build_bitcast(strings_array, i8_ptr_ptr_type, "strings_array_typed")?
            .into_pointer_value();

        // Reset current position for splitting
        self.builder
            .build_store(current_pos_alloca, string_ptr_value)?;

        // Create index for storing strings
        let store_idx_alloca = self.builder.build_alloca(i64_type, "store_idx")?;
        self.builder
            .build_store(store_idx_alloca, i64_type.const_int(0, false))?;

        self.builder.build_unconditional_branch(split_loop_start)?;

        // Split loop start
        self.builder.position_at_end(split_loop_start);
        let current_pos = self
            .builder
            .build_load(i8_ptr_type, current_pos_alloca, "current_pos")?
            .into_pointer_value();

        // Find next occurrence using strstr
        let found_ptr = self
            .builder
            .build_call(
                strstr_fn,
                &[current_pos.into(), separator_ptr.into()],
                "found_ptr",
            )?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();

        // Check if found (not NULL)
        let is_found = self.builder.build_int_compare(
            IntPredicate::NE,
            found_ptr,
            null_ptr,
            "is_found",
        )?;
        self.builder
            .build_conditional_branch(is_found, split_loop_body, add_last_part)?;

        // Split loop body: extract substring and store it
        self.builder.position_at_end(split_loop_body);
        let current_pos = self
            .builder
            .build_load(i8_ptr_type, current_pos_alloca, "current_pos")?
            .into_pointer_value();
        let found_ptr = self
            .builder
            .build_call(
                strstr_fn,
                &[current_pos.into(), separator_ptr.into()],
                "found_ptr",
            )?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();

        // Calculate substring length (found_ptr - current_pos)
        let current_pos_int =
            self.builder
                .build_ptr_to_int(current_pos, i64_type, "current_pos_int")?;
        let found_ptr_int = self
            .builder
            .build_ptr_to_int(found_ptr, i64_type, "found_ptr_int")?;
        let substring_len =
            self.builder
                .build_int_sub(found_ptr_int, current_pos_int, "substring_len")?;

        // Allocate memory for substring (+1 for null terminator)
        let alloc_size = self.builder.build_int_add(
            substring_len,
            i64_type.const_int(1, false),
            "alloc_size",
        )?;
        let substring = self
            .builder
            .build_call(malloc_fn, &[alloc_size.into()], "substring")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();

        // Copy substring using memcpy
        self.builder.build_call(
            memcpy_fn,
            &[substring.into(), current_pos.into(), substring_len.into()],
            "memcpy_call",
        )?;

        // Add null terminator
        let null_term_ptr = unsafe {
            self.builder
                .build_gep(i8_type, substring, &[substring_len], "null_term_ptr")?
        };
        self.builder
            .build_store(null_term_ptr, i8_type.const_int(0, false))?;

        // Store substring in array
        let store_idx = self
            .builder
            .build_load(i64_type, store_idx_alloca, "store_idx")?
            .into_int_value();
        let store_location = unsafe {
            self.builder.build_gep(
                i8_ptr_type,
                strings_array_typed,
                &[store_idx],
                "store_location",
            )?
        };
        self.builder.build_store(store_location, substring)?;

        // Increment store index
        let new_store_idx =
            self.builder
                .build_int_add(store_idx, i64_type.const_int(1, false), "new_store_idx")?;
        self.builder.build_store(store_idx_alloca, new_store_idx)?;

        // Move position past the separator
        let new_pos = unsafe {
            self.builder
                .build_gep(i8_type, found_ptr, &[sep_len], "new_pos")?
        };
        self.builder.build_store(current_pos_alloca, new_pos)?;

        self.builder.build_unconditional_branch(split_loop_end)?;

        // Split loop end: jump back to start
        self.builder.position_at_end(split_loop_end);
        self.builder.build_unconditional_branch(split_loop_start)?;

        // Add last part: add the remaining string after the last separator
        self.builder.position_at_end(add_last_part);
        let current_pos = self
            .builder
            .build_load(i8_ptr_type, current_pos_alloca, "current_pos")?
            .into_pointer_value();

        // Calculate remaining string length
        let remaining_len_i32 = self
            .builder
            .build_call(strlen_fn, &[current_pos.into()], "remaining_len_i32")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_int_value();
        let remaining_len =
            self.builder
                .build_int_z_extend(remaining_len_i32, i64_type, "remaining_len")?;

        // Allocate memory for last substring (+1 for null terminator)
        let last_alloc_size = self.builder.build_int_add(
            remaining_len,
            i64_type.const_int(1, false),
            "last_alloc_size",
        )?;
        let last_substring = self
            .builder
            .build_call(malloc_fn, &[last_alloc_size.into()], "last_substring")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();

        // Copy remaining string using memcpy
        self.builder.build_call(
            memcpy_fn,
            &[
                last_substring.into(),
                current_pos.into(),
                remaining_len.into(),
            ],
            "memcpy_last",
        )?;

        // Add null terminator
        let last_null_term_ptr = unsafe {
            self.builder.build_gep(
                i8_type,
                last_substring,
                &[remaining_len],
                "last_null_term_ptr",
            )?
        };
        self.builder
            .build_store(last_null_term_ptr, i8_type.const_int(0, false))?;

        // Store last substring in array
        let store_idx = self
            .builder
            .build_load(i64_type, store_idx_alloca, "store_idx")?
            .into_int_value();
        let last_store_location = unsafe {
            self.builder.build_gep(
                i8_ptr_type,
                strings_array_typed,
                &[store_idx],
                "last_store_location",
            )?
        };
        self.builder
            .build_store(last_store_location, last_substring)?;

        self.builder.build_unconditional_branch(return_block)?;

        // Return block: create and return vector struct
        self.builder.position_at_end(return_block);

        // Create PHI node for vector size
        let size_phi = self.builder.build_phi(i64_type, "vec_size_phi")?;

        // For empty separator: return vector with original string (size 1)
        size_phi.add_incoming(&[(&i64_type.const_int(1, false), empty_sep_check)]);

        // For empty string: return vector with empty string (size 1)
        size_phi.add_incoming(&[(&i64_type.const_int(1, false), empty_str_check)]);

        // For normal case: return vector with split strings
        size_phi.add_incoming(&[(&vec_size, add_last_part)]);

        // Create PHI node for array pointer
        let array_phi = self.builder.build_phi(i8_ptr_ptr_type, "array_phi")?;
        array_phi.add_incoming(&[(&empty_sep_array_typed, empty_sep_check)]);
        array_phi.add_incoming(&[(&empty_str_array_typed, empty_str_check)]);
        array_phi.add_incoming(&[(&strings_array_typed, add_last_part)]);

        let final_size = size_phi.as_basic_value().into_int_value();
        let final_array = array_phi.as_basic_value().into_pointer_value();

        // Create vector struct: {i8**, i64, i64} = {data, len, capacity}
        let vec_struct_type = self.context.struct_type(
            &[i8_ptr_ptr_type.into(), i64_type.into(), i64_type.into()],
            false,
        );

        let vec_struct = vec_struct_type.get_undef();
        let vec_struct = self
            .builder
            .build_insert_value(vec_struct, final_array, 0, "vec_with_ptr")?
            .into_struct_value();
        let vec_struct = self
            .builder
            .build_insert_value(vec_struct, final_size, 1, "vec_with_len")?
            .into_struct_value();
        let vec_struct = self
            .builder
            .build_insert_value(vec_struct, final_size, 2, "vec_with_cap")?
            .into_struct_value();

        Ok(vec_struct.into())
    }
}
